
package com.studiofortress.sf.structure;

import com.studiofortress.sf.graphics.GraphicsGL;
import com.studiofortress.sf.graphics.display.Controls;
import com.studiofortress.sf.graphics.display.GraphicsDisplay;
import com.studiofortress.sf.util.SmallPoint;

/**
 * Once this is created with a World, it cannot be changed.
 * 
 * @author Joseph Lenton
 */
final class WorldControlsInner<A extends Actor<? extends GraphicsGL>> implements WorldControls
{
    private final World world;
    private Controls controls;

    WorldControlsInner(World world)
    {
        this.world = world;
        controls = null;
    }

    /**
     * @return The controls to the current GraphicsDisplay in use.
     */
    private Controls getControls()
    {
        if (controls == null) {
            MainLoop mainLoop = getWorld().getMainLoop();

            if (mainLoop != null) {
                GraphicsDisplay g = mainLoop.getDisplay();

                if (g.isRunning()) {
                    controls = g.getControls();
                } else {
                    throw new IllegalStateException("The display for the controls is not running.");
                }
            } else {
                throw new IllegalStateException("The World for this WorldControls is not in a MainLoop.");
            }
        }
        
        return controls;
    }

    private SmallPoint getWorldOffset()
    {
        int offsetX = 0;
        int offsetY = 0;

        // go through each world above getWorld() (including the getWorld world)
        // and add all the total offsets up.
        for (World world = getWorld(); world != null; world = world.getWorld()) {
            offsetX += world.getX() - world.getWidth()/2 - world.getScrollX();
            offsetY += world.getY() - world.getHeight()/2 - world.getScrollY();
        }
        
        return new SmallPoint(offsetX, offsetY);
    }

    /**
     * @return The World object this WorldControls is for.
     */
    @Override
    public World getWorld()
    {
        return world;
    }

    @Override
    public int getMouseX()
    {
        return getControls().getMouseX();
    }

    @Override
    public int getMouseY()
    {
        return getControls().getMouseY();
    }

    @Override
    public int getMouseZ()
    {
        return getControls().getMouseZ();
    }

    @Override
    public int getWorldMouseX()
    {
        return getMouseX() - getWorldOffset().getX();
    }

    /**
     * This takes into account the position of the associated world within this
     * WorldControls.
     * @return The location of the mouse inside of this world.
     */
    @Override
    public int getWorldMouseY()
    {
        return getMouseY() - getWorldOffset().getY();
    }

    @Override
    public boolean isMouseDown(int mouseButton)
    {
        return getControls().isMouseDown(mouseButton);
    }

    @Override
    public boolean isMouseClick(int mouseButton)
    {
        return getControls().isMouseClick(mouseButton);
    }

    @Override
    public boolean isAnyMouseDown()
    {
        return getControls().isAnyMouseDown();
    }

    @Override
    public boolean isKeyDown(String key)
    {
        return getControls().isKeyDown(key);
    }

    @Override
    public boolean isKeyDown(int keyCode)
    {
        return getControls().isKeyDown( keyCode );
    }

    @Override
    public boolean isAnyKeyDown()
    {
        return getControls().isAnyKeyDown();
    }
    
    public boolean isActorUnderMouse(Actor actor)
    {
        final float mx = getWorldMouseX();
        final float my = getWorldMouseY();

        final float x = actor.getX();
        final float y = actor.getY();

        final int w = actor.getWidth()/2;
        final int h = actor.getHeight()/2;

        return ( (x-w) <= mx && mx <= (x+w) ) &&
                ( (y-h) <= my && my <= (y+h) );
    }
    
    public boolean isActorClicked(Actor actor, int mouseButton)
    {
        return isMouseClick(mouseButton) && isActorUnderMouse(actor);
    }
}
